1. Field of the Invention
The present invention relates to an improved data processing system and, in particular, to a method and apparatus for object-oriented input/output, data storage, or communication. Still more particularly, the present invention provides a method and apparatus for version management of serialized object streams.
2. Description of Related Art
While executing an object-oriented application, objects are created and then destroyed or otherwise freed; in a Java™ runtime environment, objects are eventually freed through garbage collection. In certain applications, rather than saving runtime data in persistent storage and then recreating objects, it is helpful to persist actual objects outside of the runtime environment. The object is then stored in persistent storage or communicated to another process or device.
Using object serialization, an object can be persisted and then reused. Object serialization is the process of saving an object's state to a sequence of bytes as well as the process of rebuilding those bytes into a live object, although the latter is sometimes called “deserialization”. The complete state of an object can be written to an output stream, and at some later time, the object can be recreated by reading its serialized state from an input stream. Using a three-dimensional analogy for an object, a serialized object is often called a “flattened” object, while a recreated object is sometimes called an “inflated” object.
Java™ provides default mechanisms for persisting objects. As long as an object implements the “Serializable” interface or has a class within its hierarchy that implements the interface, an object can be serialized; a Java™ class can specify which attributes are not to be serialized/deserialized using the special Java™ keyword “transient”. The “Serializable” interface does not define any methods but merely acts as a marker that indicates whether serialization is allowed on a given object. An object can by serialized by passing the object to the “writeObject( )” method of the “ObjectOutputStream” class, and the object can be recreated by calling the “readObject( )” method of the “ObjectInputStream” class. This serialization behavior can be customized to a certain extent by implementing the “writeObject( )” and “readObject( )” methods while still being able to rely on the default output and input mechanisms for writing and reading a flattened object.
Instead of relying on the default mechanism, one can create a custom serialization mechanism by implementing the “Externalizable” interface, which defines only two methods, “writeExternal( )” and “readExternal( )”. By overriding these methods, one has complete control over the writing and reading of an object's state. When a class implements the “Externalizable” interface, the “ObjectOutputStream” and “ObjectInputStream” classes use that class's “writeExternal( )” and “readExternal( )” methods to write and read the object's state to and from a flattened object stream.
One potential problem with serializing an object is that a flattened object might persist within a file for a long period of time. Meanwhile, classes may change over time such that multiple versions of a class are used while the flattened object remains in persistent storage. When an attempt is made to read the flattened object, an error could occur because the version of the class that is being used to read the flattened object is not compatible with the version of the class that was being used when the object was flattened.
The Java™ environment contains some administrative support for detecting class versioning problems. When an object is serialized, information about an object's class is also serialized so that the correct class can be loaded when the object is deserialized. Generally, this information comprises the fully-qualified name of the class; in addition, all persistent-capable classes are given a version number. The version number of a class is stored in a field named “serialVersionUID”, and the version number is used to determine whether an object can be properly deserialized; if there is a version mismatch, the “java.io.InvalidClassException” will be thrown.
If one needs to control class versioning, one can explicitly declare the “serialVersionUID” constant and ensure that it remains the same between compatible versions of a class; it should be assumed that one would not maintain the same “serialVersionUID” constant across changes to a class that break serialization compatibility. If a class does not define a “serialVersionUID” constant, then the “ObjectOutputStream” class automatically generates a unique version number for the class by applying a hashing algorithm to information related to the class, such as the class name, its interfaces, fields, and methods. Changes that break serialization compatibility can then be detected through the “serialVersionUID” constant. For example, if a non-private class method signature is changed in a new version of the class, then the automatically-computed unique version number would also change, and an exception would be thrown when an attempt was made to read a flattened object stream into an incompatible version of the object.
Although the administrative support for detecting class versioning problems is helpful to some extent, it does not provide any mechanism for recovery from this incompatibility in class versions.
Therefore, it would be advantageous to have a methodology for facilitating object serialization and deserialization such that forward and backward compatibility can be maintained for flattened objects across different versions of a class.